

// ##########################
// DataModel constructor
function DataModel() {

	// DataModel attributes

	this._highestXIndex = -1;
	this._highestYVal   = 0;
	this._highestZIndex = -1;

	this._selectedXIndex = 0;
	this._selectedZIndex = 0;
	
	// zValues stores an array of z axis index names (as strings).
	// like: [<str>,<str>,<str>,...]
	// eg ["error A", "error B", "error C"]
	this._zValues = new Array();

	// xValues stores an array of x axis index names (as strings).
	// like: [<str>,<str>,<str>,...]
	// eg ["date A", "date B", "date C"]
	this._xValues = new Array();

	// _dataPoints is a two dimensional array of data points (and each data 
	// point is an array itself).
	//
	// the first dimension: an array where each element represents a z axis index.
	// the second dimension: an array of all the data points at a particular z 
	// axis index, in order of data point's x axis index. 
	// like: 
	// [
	//       [<datapointArray_0,0>,<datapointArray0,4>,<datapointArray0,6>,...],
	//       [<datapointArray_1,0>,<datapointArray1,1>,<datapointArray1,2>,...],
	//       [<datapointArray_2,0>,<datapointArray2,1>,<datapointArray2,2>,...]
	// ]
	//
	// each data point is itself an array made up of the data point's 
	// position (x and y index) and value (z value).
	// like:
	// [<xIndexNum>, <yValNum>, <zValNum>]
	//
	this._dataPoints = new Array();
}

// ##########################
// DataModel class constants

DataModel.prototype.DATA_POINT_X_INDEX_INDEX = 0;
DataModel.prototype.DATA_POINT_Y_VALUE_INDEX = 1;
DataModel.prototype.DATA_POINT_Z_INDEX_INDEX = 2;
	
DataModel.prototype._AXIS_X = 'x';
DataModel.prototype._AXIS_Z = 'z';
	
DataModel.prototype._SELECT_DIRECTION_PREV = 'p';
DataModel.prototype._SELECT_DIRECTION_NEXT = 'n';

DataModel.prototype._TEST_DATA_NUM_OF_Z_VALS = 40;
DataModel.prototype._TEST_DATA_NUM_OF_X_VALS = 60;
DataModel.prototype._TEST_DATA_MAX_Y_VAL = 4;


// ##########################
// DataModel methods

// addDataPoint
//     description:
//         adds a data point to the data structure. 
//     params:
//         xValStr: string value for the data point's x position. (eg: "2011-01-29")
//         yValNum: number value of the data point. the y value. (eg: 4)
//         zValStr: string value for the data point's z position. (eg: "Error 124: PEBKAC detected.")
DataModel.prototype.addDataPoint = function(xValStr, yValNum, zValStr)
{
	// --------
	//determine x index for this x val. add one if necessary
	
	var xIndex = jQuery.inArray(xValStr, this._xValues); // returns index or -1
	if(xIndex == -1) { // if no existing x val matches the new one
		this._xValues.push(xValStr);      // add the new x item
		xIndex = this._xValues.length -1; // remember index of new x item
		this._highestXIndex += 1;         // remember we have a new x index
	}
	
	// --------
	//determine z index for this z val. add one if necessary
	
	var zIndex = jQuery.inArray(zValStr, this._zValues); // returns index or -1
	if(zIndex == -1) { // if no existing z val matches the new one
		this._zValues.push(zValStr);      // add the new z item
		zIndex = this._zValues.length -1; // remember index of new z item
		this._highestZIndex += 1;         // remember we have a new z index
	}
	
	// --------
	// store data point
	
	// look for existing data point
	
	var existingDataPointXAxisArrayIndex = 
		this._findDatapointByGraphIndices(xIndex, zIndex);
		
	// if data point already exists for the x and z graph indices
	if(existingDataPointXAxisArrayIndex != -1) {
	
		// update existing data point with new y value
		this._dataPoints[zIndex][existingDataPointXAxisArrayIndex][this.DATA_POINT_Y_VALUE_INDEX] = yValNum;
		
	} else { // else the data point does not exist		
		
		// if no data points exist on this z axis index, create the array
		if(this._dataPoints[zIndex] == undefined) {
			this._dataPoints[zIndex] = new Array();
		}
		
		// add data point into x axis array
		this._dataPoints[zIndex].push([xIndex, yValNum, zIndex]);
	}
	
	// if new y val is higher than any other data point's y val
	if(this._highestYVal < yValNum) {
		this._highestYVal = yValNum; // remember the value
	}
}

// returns x axis array index or -1
DataModel.prototype._findDatapointByGraphIndices = function(xIndex, zIndex) {
	
	// if no z axis items
	if(this._dataPoints==undefined) {
		return -1;
	}
	
	// if no x axis items for this z axis index
	if( this._dataPoints[zIndex]==undefined ||
		this._dataPoints[zIndex].length == 1) {
		return -1;
	}
	
	// TODO: redo this using binary search
	// for each data point at the given z index
	for(i in this._dataPoints[zIndex]) {
		// if we found the data point, return it's x axis array index
		if(this._dataPoints[zIndex][i][this.DATA_POINT_X_INDEX_INDEX] == xIndex) {
			return i;
		}
	}
	
	return -1;
}

DataModel.prototype.selectPrevOnXAxis = function() {
	this._selectIndexByDirection(this._AXIS_X, this._SELECT_DIRECTION_PREV);
}

DataModel.prototype.selectNextOnXAxis = function() {
	this._selectIndexByDirection(this._AXIS_X, this._SELECT_DIRECTION_NEXT);
}

DataModel.prototype.selectPrevOnZAxis = function() {
	this._selectIndexByDirection(this._AXIS_Z, this._SELECT_DIRECTION_PREV);
}

DataModel.prototype.selectNextOnZAxis = function() {
	this._selectIndexByDirection(this._AXIS_Z, this._SELECT_DIRECTION_NEXT);
}


//
DataModel.prototype._selectIndexByDirection = function(axis, direction) {

	var selected, maxIndex;
	if(axis == this._AXIS_X) { // if X axis
		selected = this._selectedXIndex;
		maxIndex = this._highestXIndex;
	} else if(axis == this._AXIS_Z) { // else if Z axis
		selected = this._selectedZIndex;
		maxIndex = this._highestZIndex;
	} else {
		alert("error: DataModel._selectIndexByDirection() given axis of: "+axis);
		return;
	}
	
	if(direction == this._SELECT_DIRECTION_PREV) { // if direction is previous
		if(selected <= 0) {
			this._selectIndexByValue(axis, maxIndex);
		} else {
			this._selectIndexByValue(axis, selected - 1);
		}
	} else if(direction == this._SELECT_DIRECTION_NEXT) { // if direction is next
		if(selected >= maxIndex) {
			this._selectIndexByValue(axis, 0);
		} else {
			this._selectIndexByValue(axis, selected + 1);
		}
	} else {
		alert("error: DataModel._selectIndexByDirection() given direction of: "+direction);
		return;
	}

}


DataModel.prototype._selectIndexByValue = function(axis, newIndex) {
	if(axis == this._AXIS_X) {
		this._selectedXIndex = newIndex;
	} else if(axis == this._AXIS_Z) {
		this._selectedZIndex = newIndex;
	}
}

// generateTestingData
//     description:
//         populates the model's data structure with a set of fake data for testing purposes.
DataModel.prototype.generateTestingData = function(){

	var tempZIndexNames = [];
	var tempXIndexNames = [];

	var i=0;
	for (i=0; i<=this._TEST_DATA_NUM_OF_Z_VALS; i++) {
		tempZIndexNames.push("error type " + i);
	}

	for (i=0; i<=this._TEST_DATA_NUM_OF_X_VALS; i++) {
		tempXIndexNames.push("day " + i);
	}
	
	for(x in tempXIndexNames) {
		for(z in tempZIndexNames) {
			this.addDataPoint(
				tempXIndexNames[x],
				Math.floor(Math.random()*(this._TEST_DATA_MAX_Y_VAL+1)),
				tempZIndexNames[z]);
		
		}
	}	
}


// ##########################
// DataModel getters

DataModel.prototype.getXValues = function() {
	return this._xValues;
}
DataModel.prototype.getZValues = function() {
	return this._zValues;
}
DataModel.prototype.getDataPointArrays = function() {
	return this._dataPoints;
}
DataModel.prototype.getHighestXIndex = function() {
	return this._highestXIndex;
}
DataModel.prototype.getHighestYVal = function() {
	return this._highestYVal;
}
DataModel.prototype.getHighestZIndex = function() {
	return this._highestZIndex;
}
DataModel.prototype.getSelectedXIndex = function() {
	return this._selectedXIndex;
}
DataModel.prototype.getSelectedZIndex = function() {
	return this._selectedZIndex;
}
DataModel.prototype.getSelectedDataPointXIndexName = function() {
	return this._xValues[this._selectedXIndex];
}
DataModel.prototype.getSelectedDataPointZIndexName = function() {
	return this._zValues[this._selectedZIndex];
}
DataModel.prototype.getSelectedDataPointYValue = function() {
	// for each data point on selected z index
	for(i in this._dataPoints[this._selectedZIndex]) {
		// if this data point is selected
		if( this._dataPoints[this._selectedZIndex][i][this.DATA_POINT_X_INDEX_INDEX] == this._selectedXIndex &&
			this._dataPoints[this._selectedZIndex][i][this.DATA_POINT_Z_INDEX_INDEX] == this._selectedZIndex) {
			
			return this._dataPoints[this._selectedZIndex][i][this.DATA_POINT_Y_VALUE_INDEX];
		}
	}
	
	alert("error: DataModel.getSelectedDataPointYValue() unable to find selected node (x="+this._selectedXIndex+", z="+this._selectedZIndex+") in data structure.");
}


